#!/usr/bin/perl

##############################################################################
# GNU/Linux Activation Technologies Client                                   #
#                                                                            #
# http://www.linuxgenuineadvantage.org                                       #
# https://notabug.org/GLAT/glat-client                                       #
#                                                                            #
# Copyright (C) 2007 Linux Genuine Advantage                                 #
# Copyright (C) 2019 GNU/Linux Activation Technologies                       #
#                                                                            #
# This program makes sure that a given copy of GNU/Linux is activated by     #
# using a key server.                                                        #
#                                                                            #
# This program is free software: you can redistribute it and/or modify       #
# it under the terms of the GNU General Public License as published by       #
# the Free Software Foundation, either version 3 of the License, or          #
# (at your option) any later version.                                        #
#                                                                            #
# This program is distributed in the hope that it will be useful,            #
# but WITHOUT ANY WARRANTY; without even the implied warranty of             #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              #
# GNU General Public License for more details.                               #
#                                                                            #
# You should have received a copy of the GNU General Public License          #
# along with this program.  If not, see <https://www.gnu.org/licenses/>.     #
##############################################################################

########################################
#           STANDARD MODULES           #
########################################

use strict;
use warnings;
use CGI qw( escape );
use IO::Socket::INET;

########################################
#           DECLARE VARIABLES          #
########################################

my $PROGRAM_NAME = 'glat-client';
my $VERSION = '1.1.1';

# operational mode
my $cmd = undef;

# config directory and product key locations
my $config_directory = '/etc/glat';
my $config_key_file  = $config_directory . '/product-key';

# Domain and URL to send product key and hardware hash to.
my $verify_domain	= 'localhost';
my $verify_url		= '/verify/';

# wait this long (in hours) before receiving remote instructions from a key server
my $activation_state_check_interval = 24 * 14;

########################################
#        CORE PROGRAM STRUCTURE        #
########################################

if (! defined($ARGV[0])) {
	show_usage();
}

# we have to be root to check is this GNU/Linux copy activated
check_root();

# figure out which mode to run in
$cmd = $ARGV[0];
if ('daemon' eq $cmd) {
	do_daemon();
} else {
	show_usage();
}

# if we got here, it must have worked
exit (0);

########################################
#             SUBROUTINES              #
########################################

# shows usage information and then exits
#
# accepts no arguments
# exits the program with an error condition
sub show_usage {
	print STDERR "$PROGRAM_NAME version $VERSION\n";
	print STDERR "Copyright (C) 2007 Linux Genuine Advantage\n";
	print STDERR "<http://www.linuxgenuineadvantage.org/>\n";
	print STDERR "Copyright (C) 2019 GNU/Linux Activation Technologies\n";
	print STDERR "<https://notabug.org/GLAT/glat-client>\n";
	print STDERR "\n";
	print STDERR "$PROGRAM_NAME comes with ABSOLUTELY NO WARRANTY. This is\n";
	print STDERR "free software, and you are welcome to redistribute it under certain\n";
	print STDERR "conditions. See the GNU General Public License for details.\n";
	print STDERR "\n";
	print STDERR "Usage: $PROGRAM_NAME [daemon]\n";
	print STDERR "\n";
	print STDERR "  daemon\n";
	print STDERR "\n";
	print STDERR "    Runs GNU/Linux Activation Technologies as daemon.\n";
	print STDERR "\n";
	print STDERR "Contact your key server administrator in case of activation issues.\n";
	print STDERR "\n";
	
	exit (1);
}

# shows an error message and then exits
#
# accepts an error string
# exits the program with an error condition
sub show_error {
	my $str = shift(@_);
	
	print STDERR $PROGRAM_NAME;
	if (defined($str)) {
		print STDERR ': ' . $str;
	}
	print STDERR "\n";
	exit (1);
}

# loop continuously, checking periodically to see is the given copy activated.
# if it isn't, start warning users.
#
# accepts no arguments
# runs as a daemon forever (called from inittab, does not fork into the background)
sub do_daemon {
	my $result = undef;
	
	# assume the copy is not activated
	my $is_activated = 0;
	
	# check immediately at startup, then reset the counter later
	my $hours_since_checked_activation_state = 0;
	
	# main daemon loop
	while (1) {
		

		# has it been long enough since we last checked activation state?
		# (do it every hour if the copy is not activated, so it won't take two weeks or a reboot to fix)
		if (($hours_since_checked_activation_state > $activation_state_check_interval) or (! $is_activated)) {
			
			# contact the key server and see if this GNU/Linux copy is activated
			$is_activated = check_activation_state();
			
			# reset the ticking clock
			$hours_since_checked_activation_state = 0;
			
		# count the time until we check activation state again
		} else {
			$hours_since_checked_activation_state++;
		}
	
		# the key server has decided this GNU/Linux copy is not activated
		if (! $is_activated) {
			# log to syslog
			system("logger -i -p user.info -t $PROGRAM_NAME This copy of GNU/Linux is not activated. You may be a victim of software counterfeit. See https://notabug.org/GLAT/howtotell for details.");
		}
		
		# sleep for one hour
		sleep(3600);
	}
}

# calculates an sha512sum based on the current hardware configuration
# if any of this computer's hardware changes, so will the hash (to prevent abuse).
#
# accepts no arguments
# returns an sha512sum
sub calculate_hardware_hash {
	my $sha512sum = `(ip | grep -i ether | awk '{print \$2}' | sort -u; egrep -i 'processor|vendor_id|cpu family|model|model name|stepping') /proc/cpuinfo | sha512sum | awk '{print \$1}'`;
	chomp($sha512sum);
	return ($sha512sum);
}

# checks to see if the user is root or not
#
# accepts no arguments
# returns 1 if the user is root, or exits with an error condition if they're not.
sub check_root {
	if (0 != $<) {
		show_error("This program can only activate GNU/Linux as the root user.");
	}
	
	return (1);
}

# contacts a key server to determine if the GNU/Linux copy is activated
#
# accepts no arguments
# returns 1 if the copy is activated, or 0 if it isn't
sub check_activation_state {
	
	my $result = undef;
	my $buf = undef;
	my @lines = ();
	my $past_headers = 0;
	
	# get hardware hash
	my $hardware_hash = calculate_hardware_hash();
	
	# get product key
	my $product_key = get_product_key();
	
	my $sock = IO::Socket::INET->new(
		PeerAddr => $verify_domain,
		PeerPort => 'http(80)',
		Proto    => 'tcp'
	);
	
	$result = $sock->send("GET $verify_url?hwid=" . escape($hardware_hash) . "&product_key=" . escape($product_key) . " HTTP/1.1\nHost: $verify_domain\nUser-Agent: $PROGRAM_NAME/$VERSION\n\n");
	if (! defined($result)) { return (0); }
	
	$result = $sock->recv($buf, 4096);
	if (! defined($result)) { return (0); }
	
	@lines = split(/\n/, $buf);
	foreach my $line (@lines) {
		$line =~ s/\r//go;
		$line =~ s/\n//go;
		
		# we found the blank line separating the headers from the body
		if ($line eq '') {
			$past_headers = 1;
		}
		
		# if we're looking at the message body, look for 'OK' on a line by itself
		if ($past_headers) {
			# OK
			if ($line =~ m/^OK$/o) {
				return (1);
			}
		}
	}
	
	# not activated
	return (0);
}

# gets the product key, by reading it from a file
#
# accepts no arguments
# returns the product key string (or a blank string, if not found)
sub get_product_key {
	
	my $product_key = undef;
	my $result = undef;
	
	if ( -r "$config_key_file" ) {
		$result = open(FILE, "$config_key_file");
		if ($result) {
			$product_key = <FILE>;
			close(FILE);
			chomp($product_key);
		}
		
		if ($product_key) {
			return $product_key;
		}
	}
	
	return '';
}

